
using System;

using Gdk;
using Gtk;

using Galaxium.Protocol.Xmpp.Library;
using Galaxium.Protocol.Xmpp.Library.Core;
using Galaxium.Protocol.Xmpp.Library.Xml;

namespace Galaxium.Protocol.Xmpp.GtkGui
{
	public class ProtocolLogWidget: TextView
	{
		private JabberID _jid;
		private ProtocolLog _log;
		
		public TextTag Incoming { get; private set; }
		public TextTag Outgoing { get; private set; }
		
		public TextTag Symbol { get; private set; }
		public TextTag ElementName { get; private set; }
		public TextTag AttributeName { get; private set; }
		public TextTag AttributeValue { get; private set; }
		public TextTag Namespace { get; private set; }
		public TextTag TextContent { get; private set; }
		
		public ProtocolLogWidget (ProtocolLog log, JabberID jid)
		{
			_log = log;
			_jid = jid.Bare ();
			
			Incoming = new TextTag ("incoming");
			Outgoing = new TextTag ("outgoing");
			Symbol = new TextTag ("symbol");
			ElementName = new TextTag ("element");
			AttributeName = new TextTag ("attrib_name");
			AttributeValue = new TextTag ("attrib_value");
			Namespace = new TextTag ("namespace");
			TextContent = new TextTag ("content");
			
			this.Buffer.TagTable.Add (Incoming);
			this.Buffer.TagTable.Add (Outgoing);
			this.Buffer.TagTable.Add (Symbol);
			this.Buffer.TagTable.Add (ElementName);
			this.Buffer.TagTable.Add (AttributeName);
			this.Buffer.TagTable.Add (AttributeValue);
			this.Buffer.TagTable.Add (Namespace);
			this.Buffer.TagTable.Add (TextContent);
			
			Incoming.ParagraphBackgroundGdk = new Color (255, 255, 255);
			Incoming.Family = "monospace";
			Outgoing.ParagraphBackgroundGdk = new Color (240, 240, 240);
			Outgoing.Family = "monospace";
			Symbol.ForegroundGdk = new Color (64, 64, 255);
			ElementName.ForegroundGdk = new Color (0, 0, 0);
			ElementName.Weight = Pango.Weight.Bold;
			AttributeName.ForegroundGdk = new Color (0, 0, 0);
			AttributeValue.ForegroundGdk = new Color (64, 128, 64);
			Namespace.ForegroundGdk = new Color (0, 0, 0);
			Namespace.Style = Pango.Style.Italic;
			
			foreach (LogEntry entry in log.GetLog (_jid))
				AddStanza (entry.Direction == EntryDirection.In, entry.Stanza);
			
			log.StanzaLogged += HandleStanzaLogged;
		}
		
		private void HandleStanzaLogged (object sender, LogEventArgs e)
		{
			if (e.Jid != _jid) return;
			Application.Invoke ((s, a) => AddStanza (e.Direction == EntryDirection.In, e.Stanza));
		}
		
		public void AddStanza (bool incoming, Stanza stanza)
		{
			var iter = Buffer.EndIter;
			Buffer.InsertWithTagsByName (ref iter, "\n", incoming ? "incoming" : "outgoing");
			PrintElement (incoming, stanza, 4, 0);
			iter = Buffer.EndIter;
			Buffer.InsertWithTagsByName (ref iter, "\n", incoming ? "incoming" : "outgoing");
		}
		
		private void PrintElement (bool incoming, Element elm, int indent_step, int indent_value)
		{
			var dir_tag = incoming ? "incoming" : "outgoing";
			var indent = new String (' ', indent_value);
			var iter = Buffer.EndIter;
			
			if (elm.IsAnonymous) {
				var text = (elm.Text ?? String.Empty).Trim ();
				if (text != String.Empty)
					Buffer.InsertWithTagsByName (ref iter, indent + elm.Text + "\n", dir_tag, "content");
				return;
			}
			
			Buffer.InsertWithTagsByName (ref iter, indent + "<", dir_tag, "symbol");
			Buffer.InsertWithTagsByName (ref iter, elm.Name, dir_tag, "element");
			
			foreach (var attrib in elm.Attributes) {
				Buffer.InsertWithTagsByName (ref iter, " " + attrib.Key, dir_tag, "attrib_name");
				Buffer.InsertWithTagsByName (ref iter, "=\"", dir_tag, "symbol");
				Buffer.InsertWithTagsByName (ref iter, attrib.Value, dir_tag, "attrib_value");
				Buffer.InsertWithTagsByName (ref iter, "\"", dir_tag, "symbol");
			}
			
			if (elm.IsEmpty) {
				Buffer.InsertWithTagsByName (ref iter, " />", dir_tag, "symbol");
			}
			else {
				if (elm.IsTextOnly) {
					Buffer.InsertWithTagsByName (ref iter, ">", dir_tag, "symbol");
					Buffer.InsertWithTagsByName (ref iter, elm.Text, dir_tag, "content");
				}
				else {
					Buffer.InsertWithTagsByName (ref iter, ">\n", dir_tag, "symbol");
					foreach (var child in elm)
						PrintElement (incoming, child, indent_step, indent_value + indent_step);
					iter = Buffer.EndIter;
					Buffer.InsertWithTagsByName (ref iter, indent, dir_tag);
				}
				
				Buffer.InsertWithTagsByName (ref iter, "</", dir_tag, "symbol");
				Buffer.InsertWithTagsByName (ref iter, elm.Name, dir_tag, "element");
				Buffer.InsertWithTagsByName (ref iter, ">", dir_tag, "symbol");
			}
			Buffer.InsertWithTagsByName (ref iter, "\n", dir_tag, "symbol");
		}
		
		public override void Destroy ()
		{
			_log.StanzaLogged -= HandleStanzaLogged;
			base.Destroy ();
		}
	}
}
